package pruebaciudad.panels;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.Stack;
import javax.swing.JPanel;
import pruebaciudad.formas.*;

/**
 * @author Federico Vera <fede [at] aprobe>
 */
public class PanelDeDibujo extends JPanel implements ComponentListener {
    private static final long serialVersionUID = 97876453432352325L;
    private final Stack<Figura> formas = new Stack<>();
    private final Stack<Figura> rehacer = new Stack<>();

    @SuppressWarnings("LeakingThisInConstructor")
    public PanelDeDibujo(){
        addComponentListener(this);
        setDoubleBuffered(true);
    }
    
    public void agregarFigura(Figura forma){
        synchronized (formas){
            formas.push(forma);
            rehacer.clear();
            repaint();
        }
    }
    
    public void quitarFigura(Figura forma){
        synchronized (formas){
            formas.remove(forma);
            repaint();
        }
    }
    
    public boolean puedeDeshacer(){
        return !formas.empty();
    }
    
    public boolean puedeRehacer(){
        return !rehacer.empty();
    }
    
    public void deshacer(){
        synchronized (formas){
            if (!formas.isEmpty()){
                rehacer.push(formas.pop());
                repaint();
            }
        }
    }
    
    public void rehacer(){
        synchronized (formas){
            if (!rehacer.isEmpty()){
                formas.push(rehacer.pop());
                repaint();        
            }
        }
    }
    
    private int xOffset;
    private int yOffset;
    private boolean centrarEjeX = true;
    private boolean centrarEjeY = true;
    private boolean mostrarEjeX = true;
    private boolean mostrarEjeY = true;
    private boolean mostrarBordes = true;
    private boolean mostrarCuadricula = true;

    @Override
    public void setSize(int width, int height) {
        Dimension d = getSize();
        
        if (d.width == width && d.height == height){
            return;
        }
        
        super.setSize(width, height);
        centrarEjes();
    }

    @Override
    public void setSize(Dimension d) {
        super.setSize(d);
        centrarEjes();
    }
    
    private void centrarEjes(){
        Dimension d = getSize();
        
        if (centrarEjeX){
            xOffset = d.width  / 2;
        } 
        
        if (centrarEjeY){
            yOffset = d.height / 2;
        }
    }
    
    public void setEjeX(int xOffset){
        this.xOffset = xOffset;
    }
    
    public void setOrigen(Punto p){
        xOffset = p.getX();
        yOffset = p.getY();
    }

    public boolean mostrarCuadricula() {
        return mostrarCuadricula;
    }

    public void setMostrarCuadricula(boolean mostrarCuadricula) {
        this.mostrarCuadricula = mostrarCuadricula;
        repaint();
    }

    public boolean mostrarBordes() {
        return mostrarBordes;
    }

    public void setMostrarBordes(boolean mostrarBordes) {
        this.mostrarBordes = mostrarBordes;
        repaint();
    }

    public void setCentrarEjeX(boolean centrarEjeX) {
        this.centrarEjeX = centrarEjeX;
    }

    public boolean centrarEjeY() {
        return centrarEjeY;
    }

    public void setCentrarEjeY(boolean centrarEjeY) {
        this.centrarEjeY = centrarEjeY;
    }

    public boolean mostrarEjeX() {
        return mostrarEjeX;
    }

    public void setMostrarEjeX(boolean mostrarEjeX) {
        this.mostrarEjeX = mostrarEjeX;
        repaint();
    }

    public boolean mostrarEjeY() {
        return mostrarEjeY;
    }

    public void setMostrarEjeY(boolean mostrarEjeY) {
        this.mostrarEjeY = mostrarEjeY;
        repaint();
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;
        
        mostrarCuadricula(g2d);
        mostrarEjes(g2d);
        
        for (Figura forma : formas){
            if (forma.esVisible()){
                if (forma instanceof Circulo){
                    dibujarCirculo(g2d, (Circulo)forma);
                } else if (forma instanceof Imagen){
                    dibujarImagen(g2d, (Imagen)forma);
                } else {
                    dibujarPoligono(g2d, forma);
                }
            }
        }
        
        g2d.dispose();
    }
    
    @Override
    public void componentResized(ComponentEvent e) {
        centrarEjes();
    }
    @Override
    public void componentMoved(ComponentEvent e)  {}
    @Override
    public void componentShown(ComponentEvent e)  {}
    @Override
    public void componentHidden(ComponentEvent e) {}

    private void mostrarCuadricula(Graphics2D g2d) {
        Dimension dim = getSize();
        int width = dim.width, height = dim.height;
        
        if (mostrarCuadricula){
            g2d.setColor(Color.LIGHT_GRAY);
            
            for (int i = 0; i <= height / 50; i++){
                g2d.drawLine(0, yOffset + 50 *  i, width, yOffset + 50 *  i);
                g2d.drawLine(0, yOffset + 50 * -i, width, yOffset + 50 * -i);
            }
            
            for (int i = 0; i <= dim.width  / 50; i++){
                g2d.drawLine(xOffset + 50 *  i, 0, xOffset + 50 *  i, height);
                g2d.drawLine(xOffset + 50 * -i, 0, xOffset + 50 * -i, height);
            }
        }
        
        if (mostrarBordes){
            g2d.drawLine(0, 0, width, 0);
            g2d.drawLine(0, height - 1, width, height - 1);
            g2d.drawLine(0, 0, 0, height);
            g2d.drawLine(width - 1, 0, width - 1, height);
        }
    }
    
    private void mostrarEjes(Graphics2D g2d) {
        g2d.setColor(Color.BLACK);
        Dimension d = getSize();
        
        if (mostrarEjeX){
            g2d.drawLine(0, yOffset, d.width, yOffset);
        }
        if (mostrarEjeY){
            g2d.drawLine(xOffset, 0, xOffset, d.height);
        }
    }

    private void mostrarPunto(Graphics2D g, Punto... puntos) {
        g.setColor(Color.YELLOW);
        for (Punto p : puntos){
            int x = xOffset + p.getX(), y = yOffset - p.getY();
            g.drawLine(x - 3, y    , x + 3, y);
            g.drawLine(x    , y - 3, x    , y + 3);
        }
    }

    private int[][] getArrayDeCoordenadas(Figura forma) {
        int[] coordX = new int[forma.getNumeroDePuntos()];
        int[] coordY = new int[forma.getNumeroDePuntos()];
        int i = 0;
        
        for (Punto p : forma.getPuntos()){
            coordX[i] = xOffset + p.getX();
            coordY[i] = yOffset - p.getY();
            i++;
        }
        
        return new int[][]{coordX, coordY};
    }

    private void dibujarCirculo(Graphics2D g2d, Circulo circulo) {
        int r = circulo.getRadio();
        int x = xOffset + circulo.getCentro().getX() - r;
        int y = yOffset - circulo.getCentro().getY() - r;

        r *= 2;

        if (circulo.rellenar()){
            g2d.setColor(circulo.getColorDeRelleno());
            g2d.fillOval(x, y, r, r);
        }

        if (circulo.mostrarLineas()){
            g2d.setColor(circulo.getColorDeLinea());
            g2d.drawOval(x, y, r, r);
        }

        if (circulo.mostrarCentro()){
            mostrarPunto(g2d, circulo.getCentro());
        }      
    }

    private void dibujarPoligono(Graphics2D g2d, Figura forma) {
        int[][] puntos = getArrayDeCoordenadas(forma);
            
        if (forma.rellenar()){
            g2d.setColor(forma.getColorDeRelleno());
            g2d.fillPolygon(puntos[0], puntos[1], forma.getNumeroDePuntos());
        }

        if (forma.mostrarLineas()){
            g2d.setColor(forma.getColorDeLinea());
            g2d.drawPolygon(puntos[0], puntos[1], forma.getNumeroDePuntos());
        }

        if (forma.mostrarPuntos()){
            mostrarPunto(g2d, forma.getPuntos());
        }

        if (forma instanceof FiguraConCentro){
            FiguraConCentro pol = (FiguraConCentro)forma;
            if (pol.mostrarCentro()){
                mostrarPunto(g2d, pol.getCentro());
            }
        }
    }


    private void dibujarImagen(Graphics2D g2d, Imagen imagen) {
        int[][] puntos = getArrayDeCoordenadas(imagen);
        
        int x = xOffset + imagen.getCentro().getX() - imagen.getAncho() / 2;
        int y = yOffset - imagen.getCentro().getY() - imagen.getAlto()  / 2;
        
        if (imagen.rellenar()){
            g2d.setColor(imagen.getColorDeRelleno());
            g2d.fillPolygon(puntos[0], puntos[1], imagen.getNumeroDePuntos());
        }
        
        if (imagen.mostrarLineas()){
            g2d.setColor(imagen.getColorDeLinea());
            g2d.drawPolygon(puntos[0], puntos[1], imagen.getNumeroDePuntos());
        }
        
        g2d.drawImage(imagen.getImagen(), x, y, null);
        
        if (imagen.mostrarPuntos()){
            mostrarPunto(g2d, imagen.getPuntos());
        }
        
        if (imagen.mostrarCentro()){
            mostrarPunto(g2d, imagen.getCentro());
        }
    }

}
